import ImCommon
import UIKit

// 主视图控制者
class ViewController: UIViewController, InputBoxCallback, LogonWindowCallback, RegWindowCallback, UITableViewDelegate, UNUserNotificationCenterDelegate {
    private var mChats: Dictionary<String, ChatWindow>!         // 聊天窗口集合
    private var mClient: Client!                                // 客户端
    @IBOutlet private weak var mLabelFriendCount: UILabel!      // "好友数"标签
    @IBOutlet private weak var mLabelStatus: UILabel!           // "状态"标签
    @IBOutlet private weak var mListViewUsers: UserListView!    // 用户列表控件
    private var mMenuItemAddFriend: UIAction!                   // "添加好友"菜单项
    private var mMenuItemLogoff: UIAction!                      // "离线"菜单项
    private var mMenuItemLogon: UIAction!                       // "登录"菜单项
    private var mMenuItemMChat: UIAction!                       // "群发消息"菜单项
    private var mMenuItemReg: UIAction!                         // "注册"菜单项

    /**
     * 添加所有好友到好友列表控件中。
     * @param result    登录结果
     */
    private func addAllFriends(_ result: LogonResult) {
        for i in 0 ..< result.mFriendCount {
            let userInfo = result.mFriends![i] as! UserInfo
            let isOnline = result.mFriendsOnline![i] as! Bool
            self.addOneFriend(userInfo.mUsername, iconIndex: userInfo.mIconIndex, isOnline: isOnline)
        }

        mLabelFriendCount.text = String(format: NSLocalizedString("IDS_FRIEND_COUNT", tableName: "Main", comment: ""), result.mFriendCount)
    }
    
    /**
     * 添加一个好友到好友列表控件中。
     * @param friendName    好友名
     * @param iconIndex     好友头像索引
     * @param isOnline      好友在线则为 true，否则为 false
     */
    private func addOneFriend(_ friendName: String, iconIndex: Int, isOnline: Bool) {
        if iconIndex < 0 {
            return
        }

        mListViewUsers.addItem(friendName, iconIndex: iconIndex, colorful: isOnline)
    }

    /**
     * 关闭客户端。
     */
    public func closeClient() {
        mClient.close()
    }

    /**
     * 改变界面状态。
     * @param isLogon   已登录则为 true，否则为 false
     * @return 菜单
     */
    private func changeState(_ isLogon: Bool) -> UIMenu {
        if isLogon {
            mMenuItemLogon.attributes = [.disabled]
            mMenuItemReg.attributes = [.disabled]
            mMenuItemAddFriend.attributes = []
            mMenuItemMChat.attributes = []
            mMenuItemLogoff.attributes = []
            mLabelStatus.text = NSLocalizedString("IDS_STATUS_LOGON", tableName: "Main", comment: "")
        } else {
            mMenuItemLogon.attributes = []
            mMenuItemReg.attributes = []
            mMenuItemAddFriend.attributes = [.disabled]
            mMenuItemMChat.attributes = [.disabled]
            mMenuItemLogoff.attributes = [.disabled]
            mLabelStatus.text = NSLocalizedString("IDS_STATUS_LOGOFF", tableName: "Main", comment: "")
        }
        
        mLabelFriendCount.text = String(format: NSLocalizedString("IDS_FRIEND_COUNT", tableName: "Main", comment: ""), 0)
        return UIMenu(title: "", subtitle: nil, image: nil, identifier: nil, options: [], children: [mMenuItemLogon, mMenuItemReg, mMenuItemAddFriend, mMenuItemMChat, mMenuItemLogoff])
    }
    
    /**
     * 清除所有聊天窗口。
     */
    private func clearChatWindow() {
        mChats.removeAll()
    }

    /**
     * 创建菜单。
     * @return 菜单
     */
    private func createMenu() -> UIMenu {
        // "登录"菜单项
        var title = NSLocalizedString("IDS_LOGON", tableName: "Main", comment: "")
        mMenuItemLogon = UIAction(title: title) {
            _ in
            self.onMenuItemLogonClicked()
        }
        
        // "注册"菜单项
        title = NSLocalizedString("IDS_REG", tableName: "Main", comment: "")
        mMenuItemReg = UIAction(title: title) {
            _ in
            self.onMenuItemRegClicked()
        }
        
        // "添加好友"菜单项
        title = NSLocalizedString("IDS_ADD_FRIEND", tableName: "Main", comment: "")
        mMenuItemAddFriend = UIAction(title: title, subtitle: nil, image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [.disabled]) {
            _ in
            self.onMenuItemAddFriendClicked()
        }

        // "群发消息"菜单项
        title = NSLocalizedString("IDS_MCHAT", tableName: "Main", comment: "")
        mMenuItemMChat = UIAction(title: title, subtitle: nil, image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [.disabled]) {
            _ in
            self.onMenuItemMChatClicked()
        }
        
        // "离线"菜单项
        title = NSLocalizedString("IDS_LOGOFF", tableName: "Main", comment: "")
        mMenuItemLogoff = UIAction(title: title, subtitle: nil, image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [.disabled]) {
            _ in
            self.onMenuItemLogoffClicked()
        }
        
        return UIMenu(title: "", subtitle: nil, image: nil, identifier: nil, options: [], children: [mMenuItemLogon, mMenuItemReg, mMenuItemAddFriend, mMenuItemMChat, mMenuItemLogoff])
    }

    /**
     * 获取指定好友名对应的聊天窗口。
     * @param friendName 好友名
     * @return 好友名对应的聊天窗口
     */
    private func getChatWindow(_ friendName: String) -> ChatWindow {
        // 查找用户名对应的聊天窗口
        var window = mChats[friendName]
        if window == nil {
            // 未找到，则新建一个
            window = UIStoryboard(name: "ChatWindow", bundle: nil).instantiateInitialViewController() as? ChatWindow
            window?.title = friendName
            mChats[friendName] = window
        }

        return window!
    }
    
    /**
     * 获取子视图的控制者。
     * @param viewController    视图控制者
     * @return 子视图控制者
     */
    private static func getChildViewController(_ viewController: UIViewController?) -> UIViewController? {
        if viewController == nil {
            return nil
        }

        if let presentedViewController = viewController?.presentedViewController {
            return ViewController.getChildViewController(presentedViewController)
        } else if let tabBarController = viewController as? UITabBarController {
            if let selectedViewController = tabBarController.selectedViewController {
                return ViewController.getChildViewController(selectedViewController)
            }
            return nil
        } else if let navigationController = viewController as? UINavigationController {
            return ViewController.getChildViewController(navigationController.visibleViewController)
        }

        return viewController
    }

    /**
     * 获取结果信息。
     * @param resultCode    结果码
     * @return 结果信息
     */
    private func getResultMessage(_ resultCode: ResultCode) -> String {
        switch resultCode {
            case ResultCode.ADD_FRIEND_ALLOW:
                return NSLocalizedString("IDS_ADD_FRIEND_ALLOW", tableName: "Main", comment: "")
            case ResultCode.ADD_FRIEND_NOT_ALLOW:
                return NSLocalizedString("IDS_ADD_FRIEND_NOT_ALLOW", tableName: "Main", comment: "")
            case ResultCode.ALREADY_BE_FRIENDS:
                return NSLocalizedString("IDS_ALREADY_BE_FRIENDS", tableName: "Main", comment: "")
            case ResultCode.FRIEND_NAME_EQUAL_SELF_NAME:
                return NSLocalizedString("IDS_FRIEND_NAME_EQUAL_SELF_NAME", tableName: "Main", comment: "")
            case ResultCode.LOGON_SUCCEEDED:
                return NSLocalizedString("IDS_LOGON_SUCCEEDED", tableName: "Main", comment: "")
            case ResultCode.NETWORK_ERROR:
                return NSLocalizedString("IDS_NETWORK_ERROR", tableName: "Main", comment: "")
            case ResultCode.PASSWORD_WRONG:
                return NSLocalizedString("IDS_PASSWORD_WRONG", tableName: "Main", comment: "")
            case ResultCode.REG_SUCCEEDED:
                return NSLocalizedString("IDS_REG_SUCCEEDED", tableName: "Main", comment: "")
            case ResultCode.TOO_MANY_FRIENDS:
                return NSLocalizedString("IDS_TOO_MANY_FRIENDS", tableName: "Main", comment: "")
            case ResultCode.USER_ALREADY_LOGON:
                return NSLocalizedString("IDS_USER_ALREADY_LOGON", tableName: "Main", comment: "")
            case ResultCode.USER_EXISTS:
                return NSLocalizedString("IDS_USER_EXISTS", tableName: "Main", comment: "")
            case ResultCode.USER_NOT_FOUND:
                return NSLocalizedString("IDS_USER_NOT_FOUND", tableName: "Main", comment: "")
            default:
                return ""
        }
    }

    /**
     * 获取当前屏幕上显示的视图的控制者。
     * @return 当前屏幕上显示的视图的控制者
     */
    private static func getTopViewController() -> UIViewController? {
        var window = UIApplication.shared.keyWindow
        if window?.windowLevel != .normal {
            for windowTemp in UIApplication.shared.windows {
                if windowTemp.windowLevel == .normal {
                    window = windowTemp
                    break
                }
            }
        }
 
        return ViewController.getChildViewController(window?.rootViewController)
    }

    /**
     * 聊天事件的响应方法（客户端到服务器）。
     * @param notification  通知
     */
    @objc private func onChatC2S(notification: Notification) {
        let chatInfo = notification.object as! ChatInfo
        mClient.chat(chatInfo)
    }
    
    /**
     * 聊天事件的响应方法（服务器到客户端）。
     * @param notification  通知
     */
    @objc private func onChatS2C(notification: Notification) {
        // 获取好友名
        let chatInfo = notification.object as! ChatInfo
        let isMultiChat = chatInfo.mDstUsername == ImProtocol.MULTI_CHAT
        let friendName = isMultiChat ? chatInfo.mDstUsername: chatInfo.mSrcUsername
        
        let topViewController = ViewController.getTopViewController()
        if topViewController is ChatWindow && topViewController?.title == friendName {
            // 如果聊天窗口在屏幕上，并且发送聊天信息的好友是当前聊天窗口的好友，则直接显示聊天信息
            let chatWindow = self.getChatWindow(friendName)
            chatWindow.addItem(chatInfo)
        } else {
            // 发送通知
            let content = UNMutableNotificationContent()
            content.title = isMultiChat ? chatInfo.mSrcUsername + " [ " + ImProtocol.MULTI_CHAT + " ]": chatInfo.mSrcUsername
            content.body = chatInfo.mContent
            content.sound = .default
            content.userInfo = [Const.KEY_CHAT_INFO: ImProtocol.archive(chatInfo)]
            let request = UNNotificationRequest(identifier: notification.name.rawValue, content: content, trigger: nil)
            UNUserNotificationCenter.current().add(request)
        }
    }

    /**
     * 好友离线事件的响应方法。
     * @param notification  通知
     */
    @objc private func onFriendLogoff(notification: Notification) {
        let request = notification.object as! LogoffRequest
        mListViewUsers.changeItemColor(request.mUsername, colorful: false)
    }

    /**
     * 好友登录事件的响应方法。
     * @param notification  通知
     */
    @objc private func onFriendLogon(notification: Notification) {
        let friendLogon = notification.object as! FriendLogon
        mListViewUsers.changeItemColor(friendLogon.mUsername, colorful: true)
    }

    /**
     * 得到添加好友请求事件的响应方法。
     * @param notification  通知
     */
    @objc private func onGetAddFriendRequest(notification: Notification) {
        let request = notification.object as! AddFriendRequest
        let message = request.mSrcUsername + NSLocalizedString("IDS_CONFIRM_ADD_FRIEND", tableName: "Main", comment: "")
        let titleYes = NSLocalizedString("IDS_YES", tableName: "Main", comment: "")
        let titleNo = NSLocalizedString("IDS_NO", tableName: "Main", comment: "")
        let alertDialog = UIAlertController(title: " ", message: message, preferredStyle: .alert)
        alertDialog.addAction(UIAlertAction(title: titleNo, style: .cancel, handler: {
            [self] _ in
            mClient.allowAddFriend(false, request: request)
        }))
        alertDialog.addAction(UIAlertAction(title: titleYes, style: .default, handler: {
            [self] _ in
            mClient.allowAddFriend(true, request: request)
            self.addOneFriend(request.mSrcUsername, iconIndex: request.mSrcIconIndex, isOnline: true)
            mLabelFriendCount.text = String(format: NSLocalizedString("IDS_FRIEND_COUNT", tableName: "Main", comment: ""), mClient.friendCount)
        }))
        super.present(alertDialog, animated: true)
    }

    /**
     * 得到添加好友结果事件的响应方法。
     * @param notification  通知
     */
    @objc private func onGetAddFriendResult(notification: Notification) {
        let result = notification.object as! AddFriendResult
        var message = self.getResultMessage(result.mResultCode)
        if result.mResultCode == .ADD_FRIEND_ALLOW {
            self.addOneFriend(result.mDstUsername, iconIndex: result.mDstIconIndex, isOnline: true)
            mLabelFriendCount.text = String(format: NSLocalizedString("IDS_FRIEND_COUNT", tableName: "Main", comment: ""), mClient.friendCount)
            message = String(format: message, result.mDstUsername)
            Toast.makeText(view, text: message, duration: Toast.LENGTH_SHORT).show()
        } else if result.mResultCode == .ALREADY_BE_FRIENDS || 
            result.mResultCode == .ADD_FRIEND_NOT_ALLOW ||
            result.mResultCode == .USER_NOT_FOUND {
            message = result.mDstUsername + message
            Toast.makeText(view, text: message, duration: Toast.LENGTH_LONG).show()
        }
    }

    /**
     * 得到登录结果事件的响应方法。
     * @param notification  通知
     */
    @objc private func onGetLogonResult(notification: Notification) {
        let result = notification.object as! LogonResult
        let message = self.getResultMessage(result.mResultCode)
        if result.mResultCode == .LOGON_SUCCEEDED {
            navigationItem.rightBarButtonItem?.menu = self.changeState(true)
            self.addAllFriends(result)
        } else {
            Toast.makeText(view, text: message, duration: Toast.LENGTH_LONG).show()
        }
    }

    /**
     * 得到注册结果事件的响应方法。
     * @param notification  通知
     */
    @objc private func onGetRegResult(notification: Notification) {
        let result = notification.object as! RegResult
        let message = self.getResultMessage(result.mResultCode)
        if result.mResultCode == .REG_SUCCEEDED {
            navigationItem.rightBarButtonItem?.menu = self.changeState(true)
            Toast.makeText(view, text: message, duration: Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(view, text: message, duration: Toast.LENGTH_LONG).show()
        }
    }

    internal func onInputBoxCancel() {
    }

    internal func onInputBoxOK(_ intValue: Int) {
    }

    /**
     * 输入窗口的"确定"按钮的单击响应方法。
     * @param stringValue   用户输入的文字
     */
    internal func onInputBoxOK(_ stringValue: String) {
        if stringValue.isEmpty {
            return
        }
        let resultCode = mClient.addFriend(stringValue)
        if resultCode != .SUCCEEDED {
            let message = self.getResultMessage(resultCode)
            Toast.makeText(view, text: message, duration: Toast.LENGTH_LONG).show()
        }
    }

    /**
     * 登录窗口的"确定"按钮的单击响应方法。
     * @param username  用户名
     * @param password  密码
     */
    public func onLogonWindowOK(_ username: String, password: String) {
        mClient.logon(username, password: password)
    }

    /**
     * "添加好友"菜单项的单击响应方法。
     */
    @objc private func onMenuItemAddFriendClicked() {
        let tipText = NSLocalizedString("IDS_PLEASE_INPUT_USERNAME", tableName: "Main", comment: "")
        InputBox(self, tipText: tipText, viewController: self).show()
    }

    /**
     * "离线"菜单项的单击响应方法。
     */    
    @objc private func onMenuItemLogoffClicked() {
        // 设置各种控件的状态
        navigationItem.rightBarButtonItem?.menu = self.changeState(false)

        // 清空各种列表
        mListViewUsers.clear()
        self.clearChatWindow()

        // 发送离线请求
        mClient.logoff()
    }

    /**
     * "登录"菜单项的单击响应方法。
     */
    @objc private func onMenuItemLogonClicked() {
        LogonWindow(self, viewController: self).show()
    }

    /**
     * "群发消息"菜单项的单击响应方法。
     */
    @objc private func onMenuItemMChatClicked() {
        let chatWindow = self.getChatWindow(ImProtocol.MULTI_CHAT)
        navigationController!.pushViewController(chatWindow, animated: true)
    }

    /**
     * "注册"菜单项的单击响应方法。
     */
    @objc private func onMenuItemRegClicked() {
        let regWindow = UIStoryboard(name: "RegWindow", bundle: nil).instantiateInitialViewController() as! RegWindow
        regWindow.setCallback(self)
        navigationController!.pushViewController(regWindow, animated: true)
    }

    /**
     * 网络错误事件的响应方法。
     */
    @objc private func onNetworkError() {
        let text = NSLocalizedString("IDS_NETWORK_ERROR", tableName: "Main", comment: "")
        let titleOK = NSLocalizedString("IDS_OK", tableName: "Main", comment: "")
        let alertDialog = UIAlertController(title: " ", message: text, preferredStyle: .alert)
        alertDialog.addAction(UIAlertAction(title: titleOK, style: .default, handler: {
            _ in
            exit(0)
        }))
        super.present(alertDialog, animated: true)
    }

    /**
     * 注册窗口的"确定"按钮的单击响应方法。
     * @param username  用户名
     * @param password  密码
     * @param iconIndex 头像索引
     */    
    public func onRegWindowOK(_ username: String, password: String, iconIndex: Int) {
        mClient.reg(username, password: password, iconIndex: iconIndex)
    }

    /**
     * 单击好友列表控件的响应方法。
     * @param tableView 好友列表控件
     * @param indexPath 行号
     */
    internal func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 获取用户单击的好友名
        let friendName = mListViewUsers.getItemText(itemIndex: indexPath.row)
        
        // 显示聊天窗口
        let chatWindow = self.getChatWindow(friendName)
        navigationController!.pushViewController(chatWindow, animated: true)
    }

    /**
     * 用户通知的响应方法。
     * @param center            用户通知中心
     * @param response          通知响应
     * @param completionHandler 响应方法
     */
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let data = response.notification.request.content.userInfo[Const.KEY_CHAT_INFO] as! [UInt8]
        let chatInfo = ImProtocol.unArchive(data, count: data.count) as! ChatInfo
        let isMultiChat = chatInfo.mDstUsername == ImProtocol.MULTI_CHAT
        let friendName = isMultiChat ? chatInfo.mDstUsername: chatInfo.mSrcUsername
        let chatWindow = self.getChatWindow(friendName)
        chatWindow.addItem(chatInfo)
        navigationController!.pushViewController(chatWindow, animated: true)
        completionHandler()
    }

    /**
     * 配置用户通知发起时的行为。
     * @param center            用户通知中心
     * @param notification      通知
     * @param completionHandler 响应方法
     */
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        // 显示横幅，发出提示音
        completionHandler([.banner, .sound])
    }

    /**
     * 视图加载事件的响应方法。
     */
    internal override func viewDidLoad() {
        super.viewDidLoad()
        
        // 初始化成员
        mChats = Dictionary<String, ChatWindow>()
        mListViewUsers.delegate = self
        
        // 将自身保存到应用代理中
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.viewController = self
        
        // 设置用户通知的代理
        UNUserNotificationCenter.current().delegate = self

        // 将本页面设置为首页
        let window = UIApplication.shared.delegate?.window!
        window!.rootViewController = UINavigationController(rootViewController: self)

        // 添加菜单按钮(...按钮)
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "...", style: .done, target: self, action: nil)

        // 创建菜单
        navigationItem.rightBarButtonItem!.menu = self.createMenu()
        
        // 禁用 translatesAutoresizingMaskIntoConstraints，否则控件约束将无效
        mLabelStatus.translatesAutoresizingMaskIntoConstraints = false
        mLabelFriendCount.translatesAutoresizingMaskIntoConstraints = false
        
        // 添加控件约束
        /* "状态"标签 */
        let lcLabelStatusX = NSLayoutConstraint(item: mLabelStatus!, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 0)
        view.addConstraint(lcLabelStatusX)

        let lcLabelStatusY = NSLayoutConstraint(item: mLabelStatus!, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0)
        view.addConstraint(lcLabelStatusY)

        let lblWidth = UIScreen.main.bounds.width / 2
        let lcLabelStatusWidth = NSLayoutConstraint(item: mLabelStatus!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1.0, constant: lblWidth)
        mLabelStatus.addConstraint(lcLabelStatusWidth)

        let lblHeight = UIScreen.main.bounds.height * Const.SCALE_LABEL_HEIGHT
        let lcLabelStatusHeight = NSLayoutConstraint(item: mLabelStatus!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: lblHeight)
        mLabelStatus.addConstraint(lcLabelStatusHeight)

        /* "好友数"标签 */
        let lcLabelFriendCountX = NSLayoutConstraint(item: mLabelFriendCount!, attribute: .left, relatedBy: .equal, toItem: mLabelStatus, attribute: .right, multiplier: 1.0, constant: 0)
        view.addConstraint(lcLabelFriendCountX)

        let lcLabelFriendCountY = NSLayoutConstraint(item: mLabelFriendCount!, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0)
        view.addConstraint(lcLabelFriendCountY)

        let lcLabelFriendCountWidth = NSLayoutConstraint(item: mLabelFriendCount!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1.0, constant: lblWidth)
        mLabelFriendCount.addConstraint(lcLabelFriendCountWidth)

        let lcLabelFriendCountHeight = NSLayoutConstraint(item: mLabelFriendCount!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: lblHeight)
        mLabelFriendCount.addConstraint(lcLabelFriendCountHeight)
        
        // 创建客户端
        mClient = Client()
        
        // 订阅事件
        let notificationCenter = NotificationCenter.default
        notificationCenter.addObserver(self, selector: #selector(onChatC2S), name: Const.UM_CHAT_C2S, object: nil)
        notificationCenter.addObserver(self, selector: #selector(onChatS2C), name: Const.UM_CHAT_S2C, object: nil)
        notificationCenter.addObserver(self, selector: #selector(onFriendLogoff), name: Const.UM_FRIEND_LOGOFF, object: nil)
        notificationCenter.addObserver(self, selector: #selector(onFriendLogon), name: Const.UM_FRIEND_LOGON, object: nil)
        notificationCenter.addObserver(self, selector: #selector(onGetAddFriendRequest), name: Const.UM_GET_ADD_FRIEND_REQUEST, object: nil)
        notificationCenter.addObserver(self, selector: #selector(onGetAddFriendResult), name: Const.UM_GET_ADD_FRIEND_RESULT, object: nil)
        notificationCenter.addObserver(self, selector: #selector(onGetLogonResult), name: Const.UM_GET_LOGON_RESULT, object: nil)
        notificationCenter.addObserver(self, selector: #selector(onGetRegResult), name: Const.UM_GET_REG_RESULT, object: nil)
        notificationCenter.addObserver(self, selector: #selector(onNetworkError), name: Const.UM_NETWORK_ERROR, object: nil)
    }
}
